#include "mtScreen.h"
#include <process.h>
#include <fstream>
#include <string>
#include <iostream>
#include <stdio.h>

#define TIMEOUT	10	//! how long can a finger last until it's declared as a new one

//! takes care of initialization
mtScreen::mtScreen(void)
{
	running = false;
	fliph = flipv = false;
	minx = miny = 0;
	sensitivity = 5;
	maxx = 1023;
	maxy = 767;
	FINGERTIMEOUT=8;
	wm = wiiuse_init(1);
	activeFingers[0] = activeFingers[1] = activeFingers[2] = activeFingers[3] = fliph = flipv = false;
	fingerIDs[0] = fingerIDs[1] = fingerIDs[2] = fingerIDs[3] = 0;
	activeFingersTimeout[0] = activeFingersTimeout[1] = activeFingersTimeout[2] = activeFingersTimeout[3] = TIMEOUT;
}

//! trying to free memory
mtScreen::~mtScreen(void)
{
	if(hThread)	TerminateThread(hThread, 0);
	delete listener;
	listener = NULL;
	wiiuse_disconnect(*wm);
	wiiuse_cleanup(wm, 1);
	delete wm;
	wm = NULL;
}

//! called whenever there's a new finger
void mtScreen::fingerDown(TouchData data)
{
	if(data.X>minx && data.X<maxx && data.Y>miny && data.Y<maxy) listener->fingerDown(data);
}

//! called whenever a finger is moving, that got previously registeres as new
void mtScreen::fingerUpdate(TouchData data)
{
	if(data.X>minx && data.X<maxx && data.Y>miny && data.Y<maxy) listener->fingerUpdate(data);
}

//! called when a finger got lost (ouch!)
void mtScreen::fingerUp(int id)
{
	/*if(activeFingers[id])*/ listener->fingerUp(id);
}

//! the most important interface to register an abstract-object to receive finger-events
void mtScreen::addListener(mtListener *listener)
{
	this->listener = listener;
}

//! connects the wiiuse-wiimote to the host computer
bool mtScreen::connect()
{
	if(wiiuse_find(wm,1,10))
	{
		if(wiiuse_connect(wm,1))
		{
			wiiuse_set_ir(wm[0],1);
			return true;
		}
	}
	return false;
}

//! writes the configuration file ( default is ./config.txt )
bool mtScreen::saveConfig(TouchData p1, TouchData p2, TouchData p3, TouchData p4, std::string file)
{
	if(file.length()<4) file = "config.txt";
	std::ofstream fout;
	fout.open(file.c_str(),std::ios::out);

	fliph = p1.Y<p3.Y;
	flipv = p1.X>p2.X;

	if(flipv)
	{
		minx = (p1.X>p3.X) ? (int)p1.X : (int)p3.X;
		maxx = (p2.X<p4.X) ? (int)p2.X : (int)p4.X;
	}
	else
	{
		minx = (p1.X<p3.X) ? (int)p1.X : (int)p3.X;
		maxx = (p2.X>p4.X) ? (int)p2.X : (int)p4.X;
	}

	if(fliph)
	{
		miny = (p1.Y>p2.Y) ? (int)p1.Y : (int)p2.Y;
		maxy = (p3.Y<p4.Y) ? (int)p3.Y : (int)p4.Y;
	}
	else
	{
		miny = (p1.Y<p2.Y) ? (int)p1.Y : (int)p2.Y;
		maxy = (p3.Y>p4.Y) ? (int)p3.Y : (int)p4.Y;
	}

	ConfigFile conf(file.c_str());

	conf.remove("fliph");			conf.add("fliph",fliph);
	conf.remove("flipv");			conf.add("flipv",flipv);
	conf.remove("sensitivity");		conf.add("sensitivity", sensitivity);
	conf.remove("resx");			conf.add("resx", 1024);
	conf.remove("resy");			conf.add("resy", 768);
	conf.remove("minx");			conf.add("minx", minx);
	conf.remove("maxx");			conf.add("maxx", maxx);
	conf.remove("miny");			conf.add("miny", miny);
	conf.remove("maxy");			conf.add("maxy", maxy);
	conf.remove("FingerTimeout");	conf.add("FingerTimeout", FINGERTIMEOUT);

	fout<<conf;
	fout.close();
	return true;
}

//! reads the configuration file ( default is ./config.txt )
void mtScreen::loadConfig(std::string file)
{
	if(file.length()<4) file = "config.txt";
	std::ifstream fin;

	fin.open(file.c_str(),std::ios::in);
	if( fin.is_open() )
	{
		ConfigFile conf(file.c_str());
		conf.readInto(fliph, "fliph");
		conf.readInto(flipv, "flipv");
		conf.readInto(sensitivity, "sensitivity");
		conf.readInto(FINGERTIMEOUT, "FingerTimeout");
	//	conf.readInto(resx, "resx");
	//	conf.readInto(resy, "resy");
		conf.readInto(minx, "minx");
		conf.readInto(maxx, "maxx");
		conf.readInto(miny, "miny");
		conf.readInto(maxy, "maxy");
	}
	fin.close();
}

//! starts the processing thread
void mtScreen::start()
{
	running = true;
	hThread = (HANDLE)_beginthread(&_processEntryPoint, 0, this);
}

//! the entry point for the processing thread
void mtScreen::_processEntryPoint(void * obj)
{
	((mtScreen *)obj)->process();
}

//! the real worker function for the processing thread
void mtScreen::process()
{
	static int stillstand = 0;
	static int lastFingerID = 0;
	while(running)
	{
		if(wiiuse_poll(wm,1))
		{
			stillstand = 0;
			switch(wm[0]->event)
			{
				default:
				case WIIUSE_EVENT:
					for (int i=0; i<4; i++)
					{
						if(wm[0]->ir.dot[i].visible)
						{
							TouchData td;
							td.X = wm[0]->ir.dot[i].rx;
							td.Y = wm[0]->ir.dot[i].ry;
							td.ID = i;
							td.size = wm[0]->ir.dot[i].size;
							if(!activeFingers[i])
							{
								activeFingers[i] = true;
								lastFingerID++;
								td.ID = lastFingerID;
								fingerIDs[i] = lastFingerID;
								activeFingersTimeout[i] = TIMEOUT;
								fingerDown(td);
							}
							else
							{
								td.ID = fingerIDs[i];
								fingerUpdate(td);
							}
						}
						else if(activeFingers[i])
						{
							fingerTimeout(i);
						}
					}
					break;
				case WIIUSE_DISCONNECT:
				case WIIUSE_UNEXPECTED_DISCONNECT:
					running = false;
					break;
			}
//////IS THIS CORRECT???
//			Sleep(1);
////////////////////////

		} else if (stillstand > TIMEOUT) for (int i=0; i<4; i++) if(activeFingers[i] && !wm[0]->ir.dot[i].visible)
		{
			fingerTimeout(i);
			stillstand = 0;
		}
		stillstand++;
	}
	exit(0);
}

void mtScreen::fingerTimeout(unsigned int ID)
{
	if(activeFingersTimeout[ID]>0) activeFingersTimeout[ID]--;
	else
	{
		activeFingers[ID] = false;
		fingerUp(fingerIDs[ID]);
	}
}